// ------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
// Licensed under the MIT License (MIT). See License.txt in the repo root for license information.
// ------------------------------------------------------------

#include "stdafx.h"

using namespace Common;
using namespace Federation;

HMODULE dllModule;

volatile LONG threadCount = -1;
LONG threadTestLimit = 0;

void InitializeThreadCount()
{
#if !defined(PLATFORM_UNIX)
    DWORD processId = GetCurrentProcessId();

    Handle snapshotHandle(CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ));
    if( snapshotHandle.Value == INVALID_HANDLE_VALUE )
    {
        threadCount = 1;
        return;
    }

    PROCESSENTRY32 processEntry = {};
    processEntry.dwSize = sizeof(processEntry);
    if(!Process32First(snapshotHandle.Value, &(processEntry)))
    {
        threadCount = 1;
        return;
    }

    do
    {
        if (processEntry.th32ProcessID == processId)
        {
            threadCount = processEntry.cntThreads;
            return;
        }
    }
    while(Process32Next(snapshotHandle.Value, &(processEntry)));
#endif
}

void FailFastIfThreadLimitHit()
{
	if ((0 < threadTestLimit) && (threadTestLimit < Threadpool::ActiveCallbackCount()))
	{
		// Crash as quickly as possible to preserve threadpool stacks, TestAssert may be too slow due to stack walking
		::RaiseFailFastException(nullptr, nullptr, 0);
	}
}

BOOL APIENTRY DllMain(
    HMODULE module,
    DWORD reason,
    LPVOID reserved)
{
    UNREFERENCED_PARAMETER(reserved);

    switch (reason)
    {
    case DLL_PROCESS_ATTACH:
        dllModule = module;
        break;

    case DLL_THREAD_ATTACH:
        if (threadCount < 0)
        {
            InitializeThreadCount();
        }

        ::InterlockedIncrement(&threadCount);
		
		FailFastIfThreadLimitHit();
		
        break;

    case DLL_THREAD_DETACH:
        ::InterlockedDecrement(&threadCount);
        break;

    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

//
// WARNING: 
//
// The signature below MUST exactly match (including the parameter names)
// with the signature generated by MIDL in the header file.
// Otherwise the linker does not apply the extern "C" specifier and exports 
// this function as C++ name mangled function instead of C style function.

/* [entry] */ HRESULT FabricCreateBuildLayoutSpecification( 
    /* [in] */ __RPC__in REFIID riid,
    /* [retval][out] */ __RPC__deref_out_opt void **buildLayoutSpecification)
{
    Common::DllConfig::GetConfig();

    HRESULT hr = Management::ImageModel::ComBuildLayoutSpecification::FabricCreateBuildLayoutSpecification(
        riid,
        buildLayoutSpecification);
    return ComUtility::OnPublicApiReturn(hr);
}

/* [entry] */ HRESULT FabricCreateStoreLayoutSpecification( 
    /* [in] */ __RPC__in REFIID riid,
    /* [retval][out] */ __RPC__deref_out_opt void **storeLayoutSpecification)
{
    Common::DllConfig::GetConfig();

    HRESULT hr = Management::ImageModel::ComStoreLayoutSpecification::FabricCreateStoreLayoutSpecification(
        riid,
        storeLayoutSpecification);
    return ComUtility::OnPublicApiReturn(hr);
}


/* [entry] */ HRESULT FabricCreateRunLayoutSpecification( 
    /* [in] */ __RPC__in REFIID riid,
    /* [retval][out] */ __RPC__deref_out_opt void **runLayoutSpecification)
{
    Common::DllConfig::GetConfig();

    HRESULT hr = Management::ImageModel::ComRunLayoutSpecification::FabricCreateRunLayoutSpecification(
        riid,
        runLayoutSpecification);
    return ComUtility::OnPublicApiReturn(hr);
}

/* [entry] */ HRESULT FabricCreateWinFabStoreLayoutSpecification( 
    /* [in] */ __RPC__in REFIID riid,
    /* [retval][out] */ __RPC__deref_out_opt void **winFabStoreLayoutSpecification)
{
    Common::DllConfig::GetConfig();

    HRESULT hr = Management::ImageModel::ComWinFabStoreLayoutSpecification::FabricCreateWinFabStoreLayoutSpecification(
        riid,
        winFabStoreLayoutSpecification);
    return ComUtility::OnPublicApiReturn(hr);
}

/* [entry] */ HRESULT FabricCreateFabricDeploymentSpecification( 
    /* [in] */ __RPC__in REFIID riid,
    /* [retval][out] */ __RPC__deref_out_opt void **fabricDeploymentSpecification)
{
    Common::DllConfig::GetConfig();

    HRESULT hr = Management::ImageModel::ComFabricDeploymentSpecification::FabricCreateFabricDeploymentSpecification(
        riid,
        fabricDeploymentSpecification);
    return ComUtility::OnPublicApiReturn(hr);
}

/* [entry] */ HRESULT FabricCreateWinFabRunLayoutSpecification( 
    /* [in] */ __RPC__in REFIID riid,
    /* [retval][out] */ __RPC__deref_out_opt void **winFabRunLayoutSpecification)
{
    Common::DllConfig::GetConfig();

    HRESULT hr = Management::ImageModel::ComWinFabRunLayoutSpecification::FabricCreateWinFabRunLayoutSpecification(
        riid,
        winFabRunLayoutSpecification);
    return ComUtility::OnPublicApiReturn(hr);
}

/* [entry] */ HRESULT FabricGetConfigStore( 
    /* [in] */ __RPC__in REFIID riid,
    /* [in] */ __RPC__in_opt IFabricConfigStoreUpdateHandler *updateHandler,
    /* [retval][out] */ __RPC__deref_out_opt void **configStore)
{
    // DO NOT CALL DLLCONFIG IN THE CONFIG LOAD PATH
    return ConfigLoader::FabricGetConfigStore(dllModule, riid, updateHandler, configStore);
}

/* [entry] */ HRESULT FabricGetConfigStoreEnvironmentVariable( 
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **envVariableName,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **envVariableValue)
{
    // DO NOT CALL DLLCONFIG IN THE CONFIG LOAD PATH
    return ConfigLoader::FabricGetConfigStoreEnvironmentVariable(envVariableName, envVariableValue);
}

extern ThreadErrorMessages GlobalThreadErrorMessages;

/* [entry] */ HRESULT FabricSetLastErrorMessage( 
    /* [in] */ __RPC__in LPCWSTR message,
    /* [out,retval] */ __RPC__deref_out_opt DWORD * threadId)
{
    if (threadId == NULL) { return ComUtility::OnPublicApiReturn(E_POINTER); }

    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_STRING(message)

    *threadId = GlobalThreadErrorMessages.SetMessage(parsed_message);

    return S_OK;
}

/* [entry] */ HRESULT FabricGetLastErrorMessage( 
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **result)
{
    if (result == NULL) { return ComUtility::OnPublicApiReturn(E_POINTER); }

    Common::DllConfig::GetConfig();

    auto msg = GlobalThreadErrorMessages.GetMessage();

    auto comPtr = Common::make_com<Common::ComStringResult>(msg);

    *result = comPtr.DetachNoRelease();

    return S_OK;
}

/* [entry] */ HRESULT FabricClearLastErrorMessage( 
    /* [in] */ __RPC__in DWORD threadId)
{
    Common::DllConfig::GetConfig();

    GlobalThreadErrorMessages.ClearMessage(threadId);

    return S_OK;
}

/* [entry] */ void FabricSetThreadTestLimit(LONG limit)
{
    threadTestLimit = limit;
}

/* [entry] */ LONG FabricGetThreadCount()
{
    return threadCount;
}

/* [entry] */ HRESULT FabricEncryptText(
    /* [in] */ __RPC__in_string LPCWSTR text,
    /* [in] */ __RPC__in_string LPCWSTR certThumbPrint,
    /* [in] */ __RPC__in_string LPCWSTR certStoreName,
    /* [in] */ __RPC__in FABRIC_X509_STORE_LOCATION certStoreLocation,
    /* [in] */ __RPC__in_opt_string LPCSTR algorithmOid,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **encryptedValue)
{
    Common::DllConfig::GetConfig();

    if (certThumbPrint == NULL || certStoreLocation == NULL || text == NULL)
    {
        return ComUtility::OnPublicApiReturn(E_POINTER);
    }

    X509StoreLocation::Enum storeLocationNative;
    ErrorCode error = X509StoreLocation::FromPublic(certStoreLocation, storeLocationNative);
    if (!error.IsSuccess())
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }

    TRY_COM_PARSE_PUBLIC_STRING(text)
    TRY_COM_PARSE_PUBLIC_STRING(certThumbPrint)
    TRY_COM_PARSE_PUBLIC_STRING_ALLOW_NULL(certStoreName)

    std::wstring result;
    error = CryptoUtility::EncryptText(
        parsed_text,
        parsed_certThumbPrint,
        parsed_certStoreName,
        storeLocationNative,
        algorithmOid,
        result);

    if (error.IsSuccess())
    {
        return ComStringResult::ReturnStringResult(result, encryptedValue);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricEncryptText2(
    /* [in] */ __RPC__in LPCWSTR text,
    /* [in] */ __RPC__in LPCWSTR certFilePath,
    /* [in] */ __RPC__in LPCSTR algorithmOid,
    /* [retval][out] */ __RPC__deref_out_opt IFabricStringResult **encryptedValue)
{
    Common::DllConfig::GetConfig();

    if (certFilePath == NULL || text == NULL)
    {
        return ComUtility::OnPublicApiReturn(E_POINTER);
    }

    TRY_COM_PARSE_PUBLIC_STRING(text)
    TRY_COM_PARSE_PUBLIC_FILEPATH(certFilePath)

    std::wstring result;
    ErrorCode error = CryptoUtility::EncryptText(
        parsed_text,
        parsed_certFilePath,
        algorithmOid,
        result);

    if (error.IsSuccess())
    {
        return ComStringResult::ReturnStringResult(result, encryptedValue);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricDecryptText(
    /* [in] */ __RPC__in_string LPCWSTR encryptedText,
    /* [in] */ FABRIC_X509_STORE_LOCATION certStoreLocation,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **decryptedText)
{
    Common::DllConfig::GetConfig();

    if (encryptedText == NULL)
    {
        return ComUtility::OnPublicApiReturn(E_POINTER);
    }

    X509StoreLocation::Enum storeLocationNative;
    ErrorCode error = X509StoreLocation::FromPublic(certStoreLocation, storeLocationNative);
    if (!error.IsSuccess())
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }

    TRY_COM_PARSE_PUBLIC_LONG_STRING(encryptedText)

    Common::SecureString result;
    error = CryptoUtility::DecryptText(parsed_encryptedText, storeLocationNative, result);
    if (error.IsSuccess())
    {
        return ComSecureStringResult::ReturnStringResult(result, decryptedText);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricDecryptValue(
    /* [in] */ __RPC__in LPCWSTR encryptedValue,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **decryptedValue)
{
    Common::DllConfig::GetConfig();

    if (encryptedValue == NULL)
    {
        return ComUtility::OnPublicApiReturn(E_POINTER);
    }

    TRY_COM_PARSE_PUBLIC_LONG_STRING(encryptedValue)

    Common::SecureString result;
    ErrorCode error = CryptoUtility::DecryptText(parsed_encryptedValue, result);

    if (error.IsSuccess())
    {
        return ComSecureStringResult::ReturnStringResult(result, decryptedValue);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricEncryptValue( 
    /* [in] */ __RPC__in LPCWSTR certThumbPrint,
    /* [in] */ __RPC__in LPCWSTR certStoreName,
    /* [in] */ __RPC__in LPCWSTR text,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **encryptedValue)
{
    Common::DllConfig::GetConfig();

    if (certThumbPrint == NULL || text == NULL)
    {
        return ComUtility::OnPublicApiReturn(E_POINTER);
    }

    TRY_COM_PARSE_PUBLIC_STRING(text)
    TRY_COM_PARSE_PUBLIC_STRING(certThumbPrint)
    TRY_COM_PARSE_PUBLIC_STRING_ALLOW_NULL(certStoreName)

    std::wstring result;
    ErrorCode error = CryptoUtility::EncryptText(
        parsed_text,
        parsed_certThumbPrint,
        parsed_certStoreName,
        X509Default::StoreLocation(),
        nullptr,
        result);

    if(error.IsSuccess())
    {
        return ComStringResult::ReturnStringResult(result, encryptedValue);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricIsValidExpression( 
    /* [in] */ __RPC__in LPCWSTR expression,
    /* [out,retval] */ __RPC__deref_out_opt BOOLEAN * isValid)
{
    Common::DllConfig::GetConfig();

    if(expression == NULL)
    {
        return ComUtility::OnPublicApiReturn(E_POINTER);
    }

    TRY_COM_PARSE_PUBLIC_STRING(expression)

    *isValid = (Expression::Build(parsed_expression) != nullptr);
    return ComUtility::OnPublicApiReturn(S_OK);
}

#if defined(PLATFORM_UNIX)
/* [entry] */ HRESULT GetLinuxPackageManagerType(
    /* [out] */ __RPC__out int32_t * packageManagerType)
{
    Common::DllConfig::GetConfig();

    Common::LinuxPackageManagerType::Enum type;
    ErrorCode error = FabricEnvironment::GetLinuxPackageManagerType(type);

    if (error.IsSuccess())
    {
        *packageManagerType = static_cast<int32_t>(type);
    }

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}
#endif

/* [entry] */ HRESULT FabricGetRoot2(
    /* [in] */ __RPC__in LPCWSTR machineName,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricRoot)
{
    Common::DllConfig::GetConfig();

    std::wstring result;
    ErrorCode error = FabricEnvironment::GetFabricRoot(machineName, result);

    if (error.IsSuccess())
    {
        return ComStringResult::ReturnStringResult(result, fabricRoot);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricGetRoot(
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricRoot)
{
    return FabricGetRoot2(NULL, fabricRoot);
}

/* [entry] */ HRESULT FabricGetBinRoot2(
     /* [in] */ __RPC__in LPCWSTR machineName, 
     /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricBinRoot)
{
    Common::DllConfig::GetConfig();

    std::wstring result;
    ErrorCode error = FabricEnvironment::GetFabricBinRoot(machineName, result);

    if (error.IsSuccess())
    {
        return ComStringResult::ReturnStringResult(result, fabricBinRoot);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricGetBinRoot(
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricBinRoot)
{
    return FabricGetBinRoot2(NULL, fabricBinRoot);
}

/* [entry] */ HRESULT FabricGetCodePath2(
     /* [in] */ __RPC__in LPCWSTR machineName, 
     /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricCodePath)
{
    Common::DllConfig::GetConfig();

    std::wstring result;
    ErrorCode error = FabricEnvironment::GetFabricCodePath(dllModule, machineName, result);

    if (error.IsSuccess())
    {
        return ComStringResult::ReturnStringResult(result, fabricCodePath);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricGetCodePath(
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricCodePath)
{
    return FabricGetCodePath2(NULL, fabricCodePath);
}

/* [entry] */ HRESULT FabricGetDataRoot2(
    /* [in] */ __RPC__in LPCWSTR machineName, 
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricDataRoot)
{
    Common::DllConfig::GetConfig();

    std::wstring result;
    ErrorCode error = FabricEnvironment::GetFabricDataRoot(machineName, result);

    if (error.IsSuccess())
    {
        return ComStringResult::ReturnStringResult(result, fabricDataRoot);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricGetDataRoot(
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricDataRoot)
{
    return FabricGetDataRoot2(NULL, fabricDataRoot);
}

/* [entry] */ HRESULT FabricGetLogRoot2(
     /* [in] */ __RPC__in LPCWSTR machineName, 
     /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricLogRoot)
{
    Common::DllConfig::GetConfig();

    std::wstring result;
    ErrorCode error = FabricEnvironment::GetFabricLogRoot(machineName, result);

    if (error.IsSuccess())
    {
        return ComStringResult::ReturnStringResult(result, fabricLogRoot);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT FabricGetLogRoot(
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **fabricLogRoot)
{
    return FabricGetLogRoot2(NULL, fabricLogRoot);
}

/* [entry] */ HRESULT FabricSetRoot2(
    /* [in] */ __RPC__in LPCWSTR fabricRoot, 
    /* [in] */ __RPC__in LPCWSTR machineName)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(fabricRoot)

    ErrorCode error = FabricEnvironment::SetFabricRoot(parsed_fabricRoot, machineName);
    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricSetRoot(
    /* [in] */ __RPC__in LPCWSTR fabricRoot)
{
    return FabricSetRoot2(fabricRoot, NULL);
}

/* [entry] */ HRESULT FabricSetBinRoot2(
    /* [in] */ __RPC__in LPCWSTR binRoot, 
    /* [in] */ __RPC__in LPCWSTR machineName)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(binRoot)

    ErrorCode error = FabricEnvironment::SetFabricBinRoot(parsed_binRoot, machineName);
    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricSetBinRoot(
    /* [in] */ __RPC__in LPCWSTR binRoot)
{
    return FabricSetBinRoot2(binRoot, NULL);
}

/* [entry] */ HRESULT FabricSetCodePath2(
    /* [in] */ __RPC__in LPCWSTR codePath, 
    /* [in] */ __RPC__in LPCWSTR machineName)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(codePath)

    ErrorCode error = FabricEnvironment::SetFabricCodePath(parsed_codePath, machineName);
    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricSetCodePath(
    /* [in] */ __RPC__in LPCWSTR codePath)
{
    return FabricSetCodePath2(codePath, NULL);
}

/* [entry] */ HRESULT FabricSetDataRoot2(
    /* [in] */ __RPC__in LPCWSTR dataRoot, 
    /* [in] */ __RPC__in LPCWSTR machineName)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(dataRoot)
    TRY_COM_PARSE_PUBLIC_STRING_ALLOW_NULL(machineName)

    ErrorCode error = FabricEnvironment::SetFabricDataRoot(parsed_dataRoot, machineName);
    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricSetDataRoot(
    /* [in] */ __RPC__in LPCWSTR dataRoot)
{
    return FabricSetDataRoot2(dataRoot, NULL);
}

/* [entry] */ HRESULT FabricSetLogRoot2( 
    /* [in] */ __RPC__in LPCWSTR logRoot, 
    /* [in] */ __RPC__in LPCWSTR machineName)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(logRoot)

    ErrorCode error = FabricEnvironment::SetFabricLogRoot(parsed_logRoot, machineName);
    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricSetLogRoot( 
    /* [in] */ __RPC__in LPCWSTR logRoot)
{
    return FabricSetLogRoot2(logRoot, NULL);
}

#if !defined(PLATFORM_UNIX)
/* [entry] */ HRESULT FabricGetCommonDllVersion( 
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **commonDllVersion)
{
    Common::DllConfig::GetConfig();

    std::wstring result;
    auto error = Environment::GetCurrentModuleFileVersion2(result);

    if(error.IsSuccess())
    {
        return ComStringResult::ReturnStringResult(result, commonDllVersion);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}
#endif

/* [entry] */ HRESULT FabricDirectoryCreate(
    /* [in] */ __RPC__in LPCWSTR path)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    auto error = Directory::Create2(parsed_path);

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricDirectoryGetDirectories(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [in] */ __RPC__in LPCWSTR pattern,
    /* [in] */ __RPC__in BOOLEAN getFullPath,
    /* [in] */ __RPC__in BOOLEAN topDirectoryOnly,
    /* [retval][out] */ IFabricStringListResult **result)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)
    TRY_COM_PARSE_PUBLIC_STRING(pattern)

    auto subdirectories = Directory::GetSubDirectories(parsed_path, parsed_pattern, getFullPath == TRUE, topDirectoryOnly == TRUE);

    return ComStringCollectionResult::ReturnStringCollectionResult(std::move(subdirectories), result);
}

/* [entry] */ HRESULT FabricDirectoryGetFiles(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [in] */ __RPC__in LPCWSTR pattern,
    /* [in] */ __RPC__in BOOLEAN getFullPath,
    /* [in] */ __RPC__in BOOLEAN topDirectoryOnly,
    /* [retval][out] */ IFabricStringListResult **result)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)
    TRY_COM_PARSE_PUBLIC_STRING(pattern)

    auto files = Directory::GetFiles(parsed_path, parsed_pattern, getFullPath == TRUE, topDirectoryOnly == TRUE);

    return ComStringCollectionResult::ReturnStringCollectionResult(std::move(files), result);
}

/* [entry] */ HRESULT FabricDirectoryCopy(
    /* [in] */ __RPC__in LPCWSTR src,
    /* [in] */ __RPC__in LPCWSTR des,
    /* [in] */ __RPC__in BOOLEAN overwrite)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(src)
    TRY_COM_PARSE_PUBLIC_FILEPATH(des)

    auto error = Directory::Copy(parsed_src, parsed_des, overwrite == TRUE);

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricDirectoryRename(
    /* [in] */ __RPC__in LPCWSTR src,
    /* [in] */ __RPC__in LPCWSTR des,
    /* [in] */ __RPC__in BOOLEAN overwrite)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(src)
    TRY_COM_PARSE_PUBLIC_FILEPATH(des)

    auto error = Directory::Rename(parsed_src, parsed_des, overwrite == TRUE);

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricDirectoryExists(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [out,retval] */ __RPC__deref_out_opt BOOLEAN * isExisted)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    *isExisted = Directory::Exists(parsed_path);

    return ComUtility::OnPublicApiReturn(S_OK);
}

/* [entry] */ HRESULT FabricDirectoryDelete(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [in] */ __RPC__in BOOLEAN recursive,
    /* [in] */ __RPC__in BOOLEAN deleteReadOnlyFiles)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    auto error = Directory::Delete(parsed_path, recursive == TRUE, deleteReadOnlyFiles == TRUE);

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricDirectoryIsSymbolicLink(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [out,retval] */ __RPC__deref_out_opt BOOLEAN * result)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    *result = (Directory::IsSymbolicLink(parsed_path) ? TRUE : FALSE);

    return ComUtility::OnPublicApiReturn(S_OK);
}

/* [entry] */ HRESULT FabricFileOpen(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [in] */ __RPC__in FABRIC_FILE_MODE fileMode,
    /* [in] */ __RPC__in FABRIC_FILE_ACCESS fileAccess,
    /* [in] */ __RPC__in FABRIC_FILE_SHARE fileShare,
    /* [out,retval] */ __RPC__deref_out_opt HANDLE * fileHandle)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    File file;
    auto error = file.Open(parsed_path, fileMode, fileAccess, fileShare, fileHandle);
    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricFileOpenEx(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [in] */ __RPC__in FABRIC_FILE_MODE fileMode,
    /* [in] */ __RPC__in FABRIC_FILE_ACCESS fileAccess,
    /* [in] */ __RPC__in FABRIC_FILE_SHARE fileShare,
    /* [in] */ __RPC__in FABRIC_FILE_ATTRIBUTES fileAttributes,
    /* [out,retval] */ __RPC__deref_out_opt HANDLE * fileHandle)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    File file;
    auto error = file.Open(parsed_path, fileMode, fileAccess, fileShare, fileAttributes, fileHandle);
    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricFileCopy(
    /* [in] */ __RPC__in LPCWSTR src,
    /* [in] */ __RPC__in LPCWSTR des,
    /* [in] */ __RPC__in BOOLEAN overwrite)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(src)
    TRY_COM_PARSE_PUBLIC_FILEPATH(des)

    auto error = File::Copy(parsed_src, parsed_des, overwrite == TRUE);

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricFileMove(
    /* [in] */ __RPC__in LPCWSTR src,
    /* [in] */ __RPC__in LPCWSTR des)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(src)
    TRY_COM_PARSE_PUBLIC_FILEPATH(des)

    auto error = File::MoveTransacted(parsed_src, parsed_des, false);

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricFileExists(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [out,retval] */ __RPC__deref_out_opt BOOLEAN * isExisted)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    *isExisted = File::Exists(parsed_path);

    return ComUtility::OnPublicApiReturn(S_OK);
}

/* [entry] */ HRESULT FabricFileDelete(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [in] */ __RPC__in BOOLEAN deleteReadonly)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)
    
    auto error = File::Delete2(parsed_path, deleteReadonly == TRUE);

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricFileRemoveReadOnlyAttribute(
	/* [in] */ __RPC__in LPCWSTR path)
{
	Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

	auto error = File::RemoveReadOnlyAttribute(parsed_path);

	return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricFileReplace(
    /* [in] */ __RPC__in LPCWSTR lpReplacedFileName,
    /* [in] */ __RPC__in LPCWSTR lpReplacementFileName,
    /* [in] */ __RPC__in LPCWSTR lpBackupFileName,
    /* [in] */ BOOLEAN bIgnoreMergeErrors)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(lpReplacedFileName)
    TRY_COM_PARSE_PUBLIC_FILEPATH(lpReplacementFileName)
    TRY_COM_PARSE_PUBLIC_FILEPATH(lpBackupFileName)

    auto error = File::Replace(
        parsed_lpReplacedFileName,
        parsed_lpReplacementFileName,
        parsed_lpBackupFileName,
        bIgnoreMergeErrors == TRUE);

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricFileCreateHardLink(
    /* [in] */ __RPC__in LPCWSTR fileName,
    /* [in] */ __RPC__in LPCWSTR existingFileName,
    /* [out,retval] */ __RPC__deref_out_opt BOOLEAN * succeeded)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(fileName)
    TRY_COM_PARSE_PUBLIC_FILEPATH(existingFileName)

    *succeeded = File::CreateHardLink(parsed_fileName, parsed_existingFileName);

    return ComUtility::OnPublicApiReturn(S_OK);
}

/* [entry] */ HRESULT FabricFileGetSize(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [out,retval] */ __RPC__out int64 * size)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    File file;
    auto error = file.TryOpen(parsed_path, FileMode::Open, FileAccess::Read, FileShare::Read);
    if (error.IsSuccess())
    {
        *size = file.size();
    }

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricFileGetLastWriteTime(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [out,retval] */ __RPC__out FILETIME * lastWriteTime)
{
    Common::DllConfig::GetConfig();
    
    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    File file;
    auto error = file.TryOpen(parsed_path);
    if (error.IsSuccess())
    {
        file.GetWriteTime(*lastWriteTime);
    }

    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

#if !defined(PLATFORM_UNIX)
/* [entry] */ HRESULT FabricFileGetVersionInfo(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **versionResult)
{
    Common::DllConfig::GetConfig();
    
    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    auto version = FileVersion::GetFileVersion(parsed_path);

    return ComStringResult::ReturnStringResult(version, versionResult);
}
#endif

/* [entry] */ HRESULT FabricGetUncPath(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **uncPath)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    std::wstring result = Path::ConvertToNtPath(parsed_path);

    return ComStringResult::ReturnStringResult(result, uncPath);
}

/* [entry] */ HRESULT FabricGetDirectoryName(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **directoryName)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    std::wstring result = Path::GetDirectoryName(parsed_path);

    return ComStringResult::ReturnStringResult(result, directoryName);
}

/* [entry] */ HRESULT FabricGetFullPath(
    /* [in] */ __RPC__in LPCWSTR path,
    /* [out] */ __RPC__deref_out_opt IFabricStringResult **fullPath)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(path)

    std::wstring result = Path::GetFullPath(parsed_path);

    return ComStringResult::ReturnStringResult(result, fullPath);
}

/* [entry] */ HRESULT FabricGetNodeIdFromNodeName(
    /* [in] */ __RPC__in LPCWSTR nodeName,
    /* [in] */ __RPC__in LPCWSTR rolesForWhichToUseV1Generator,
    /* [in] */ __RPC__in BOOLEAN useV2NodeIdGenerator,
    /* [in] */ __RPC__in LPCWSTR nodeIdGeneratorVersion,
    /* [out] */ __RPC__deref_out_opt FABRIC_NODE_ID * fabricNodeId)
{
    Common::DllConfig::GetConfig();

    NodeId nodeId;
    nodeId.FromPublicApi(*fabricNodeId);

    TRY_COM_PARSE_PUBLIC_STRING(nodeName)
    TRY_COM_PARSE_PUBLIC_STRING_ALLOW_NULL(rolesForWhichToUseV1Generator)
    TRY_COM_PARSE_PUBLIC_STRING(nodeIdGeneratorVersion)

    ErrorCode error = NodeIdGenerator::GenerateFromString(
        parsed_nodeName, 
        nodeId, 
        parsed_rolesForWhichToUseV1Generator, 
        useV2NodeIdGenerator == TRUE, 
        parsed_nodeIdGeneratorVersion);

    if (error.IsSuccess())
    {
        nodeId.ToPublicApi(*fabricNodeId);
        return ComUtility::OnPublicApiReturn(S_OK);
    }
    else
    {
        return ComUtility::OnPublicApiReturn(error.ToHResult());
    }
}

/* [entry] */ HRESULT CabExtractFiltered(
    /* [in] */ __RPC__in LPCWSTR cabPath,
    /* [in] */ __RPC__in LPCWSTR extractPath,
    /* [in] */ __RPC__in LPCWSTR filters,
    /* [in] */ __RPC__in BOOLEAN inclusive)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(cabPath)
    TRY_COM_PARSE_PUBLIC_FILEPATH(extractPath)

    std::vector<std::wstring> vectorFilters;
    std::wistringstream ss(filters);
    std::wstring token;
    while (std::getline(ss, token, L',')) 
    {
        vectorFilters.push_back(token);
    }

    auto error = CabOperations::ExtractFiltered(parsed_cabPath, parsed_extractPath, vectorFilters, inclusive == TRUE);

    return ComUtility::OnPublicApiReturn((HRESULT)error);
}

/* [entry] */ HRESULT IsCabFile(
/* [in] */ __RPC__in LPCWSTR cabPath,
/* [out,retval] */ __RPC__deref_out_opt BOOLEAN * isCab)
{
    Common::DllConfig::GetConfig();

    TRY_COM_PARSE_PUBLIC_FILEPATH(cabPath)

    *isCab = CabOperations::IsCabFile(parsed_cabPath);

    return ComUtility::OnPublicApiReturn(S_OK);
}

#ifndef PLATFORM_UNIX

/* [entry] */ HRESULT GenerateSelfSignedCertAndImportToStore(
	/* [in] */ __RPC__in LPCWSTR subName,
	/* [in] */ __RPC__in LPCWSTR storeName,
	/* [in] */ __RPC__in LPCWSTR profile,
	/* [in] */ __RPC__in LPCWSTR DNS,
	/* [in] */ __RPC__in FILETIME expireDate)
	{
	Common::DllConfig::GetConfig();

	TRY_COM_PARSE_PUBLIC_STRING(subName)
	TRY_COM_PARSE_PUBLIC_STRING(storeName)
	TRY_COM_PARSE_PUBLIC_STRING(profile)

	CertificateManager *cf;

	if (DNS != NULL)
	{
        TRY_COM_PARSE_PUBLIC_STRING(DNS)

		if (expireDate.dwHighDateTime != 0)
		{
			DateTime dtExpireDate(expireDate);
			cf = new CertificateManager(parsed_subName, parsed_DNS, dtExpireDate);
		}
		else
		{
			cf = new CertificateManager(parsed_subName, parsed_DNS);
		}
	}
	else if (expireDate.dwHighDateTime != 0)
	{
		DateTime dtExpireDate(expireDate);
		cf = new CertificateManager(parsed_subName, dtExpireDate);
	}
	else
	{
		cf = new CertificateManager(parsed_subName);
	}

	auto error = cf->ImportToStore(parsed_storeName, parsed_profile);
	return ComUtility::OnPublicApiReturn(error.ToHResult());
}


/* [entry] */ HRESULT GenerateSelfSignedCertAndSaveAsPFX(
	/* [in] */ __RPC__in LPCWSTR subName,
	/* [in] */ __RPC__in LPCWSTR fileName,
	/* [in] */ __RPC__in LPCWSTR password,
	/* [in] */ __RPC__in LPCWSTR DNS,
	/* [in] */ __RPC__in FILETIME expireDate)
	{
	Common::DllConfig::GetConfig();

	TRY_COM_PARSE_PUBLIC_STRING(subName)
	TRY_COM_PARSE_PUBLIC_STRING(fileName)
	TRY_COM_PARSE_PUBLIC_STRING_ALLOW_NULL(password)

	SecureString pwd(move(parsed_password));

	CertificateManager *cf;
	if (DNS != NULL)
	{
        TRY_COM_PARSE_PUBLIC_STRING(DNS)

		if (expireDate.dwHighDateTime != 0)
		{
			DateTime dtExpireDate(expireDate);
			cf = new CertificateManager(parsed_subName, parsed_DNS, dtExpireDate);
		}
		else
		{
			cf = new CertificateManager(parsed_subName, parsed_DNS);
		}
	}
	else if (expireDate.dwHighDateTime != 0)
	{
		DateTime dtExpireDate(expireDate);
		cf = new CertificateManager(parsed_subName, dtExpireDate);
	}
	else
	{
		cf = new CertificateManager(parsed_subName);
	}

	auto error = cf->SaveAsPFX(fileName, pwd);
	return ComUtility::OnPublicApiReturn(error.ToHResult());

	}


/* [entry] */ HRESULT DeleteCertificateFromStore(
	/* [in] */ __RPC__in LPCWSTR certName,
	/* [in] */ __RPC__in LPCWSTR store,
	/* [in] */ __RPC__in LPCWSTR profile,
	/* [in] */ __RPC__in BOOLEAN isExactMatch)
{
	Common::DllConfig::GetConfig();

	TRY_COM_PARSE_PUBLIC_STRING(certName)
	TRY_COM_PARSE_PUBLIC_STRING(store)
	TRY_COM_PARSE_PUBLIC_STRING(profile)

	auto error = CertificateManager::DeleteCertFromStore(parsed_certName, parsed_store, parsed_profile, isExactMatch == TRUE);
	return ComUtility::OnPublicApiReturn(error.ToHResult());
}
#endif

/* [entry] */ HRESULT VerifyFileSignature(
	/* [in] */ __RPC__in LPCWSTR filename,
	/* [out,retval] */ __RPC__deref_out_opt BOOLEAN * isValid)
{
#ifdef PLATFORM_UNIX
	ErrorCode error;
#else
	TRY_COM_PARSE_PUBLIC_FILEPATH(filename)

    auto error = CryptoUtility::VerifyEmbeddedSignature(parsed_filename);
#endif
    *isValid = error.IsSuccess();
	return ComUtility::OnPublicApiReturn(error.ToHResult());
}


/* [entry] */ HRESULT WriteManagedTrace(
    /* [in] */ __RPC__in LPCWSTR taskName,
    /* [in] */ __RPC__in LPCWSTR eventName,
    /* [in] */ __RPC__in LPCWSTR id,
    /* [in] */ USHORT level,
    /* [in] */ __RPC__in LPCWSTR text)
{
    TRY_COM_PARSE_PUBLIC_STRING(taskName)
    TRY_COM_PARSE_PUBLIC_STRING(eventName)
    TRY_COM_PARSE_PUBLIC_STRING(id)
    TRY_COM_PARSE_PUBLIC_LONG_STRING(text)

    LogLevel::Enum logLevel = (LogLevel::Enum)(level);
    std::wstring wsType = parsed_taskName + L"." + parsed_eventName;

    std::string sType(wsType.begin(), wsType.end());

    StringLiteral const type(sType.c_str(), sType.c_str() + sType.length()); 
    switch (logLevel) 
    {
    case LogLevel::Enum::Critical:
    case LogLevel::Enum::Error:
        TraceError(TraceTaskCodes::Managed, type, parsed_id, "{0}", parsed_text);
        break;
    case LogLevel::Enum::Info:
        TraceInfo(TraceTaskCodes::Managed, type, parsed_id, "{0}", parsed_text);
        break;
    case LogLevel::Enum::Noise:
        TraceNoise(TraceTaskCodes::Managed, type, parsed_id, "{0}", parsed_text);
        break;
    case LogLevel::Enum::Warning:
        TraceWarning(TraceTaskCodes::Managed, type, parsed_id, "{0}", parsed_text);
        break;
    default:
        TraceInfo(TraceTaskCodes::Managed, type, parsed_id, "{0}", parsed_text);
        break;
    };

    return ComUtility::OnPublicApiReturn(S_OK);
}

/* [entry] */ HRESULT FabricSetEnableCircularTraceSession2(
    /* [in] */ BOOLEAN enableCircularTraceSession, 
    /* [in] */ __RPC__in LPCWSTR machineName)
{
    Common::DllConfig::GetConfig();

    ErrorCode error = FabricEnvironment::SetEnableCircularTraceSession(enableCircularTraceSession, machineName);
    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricSetEnableCircularTraceSession(
    /* [in] */ BOOLEAN enableCircularTraceSession)
{
    return FabricSetEnableCircularTraceSession2(enableCircularTraceSession, NULL);
}

/* [entry] */ HRESULT FabricGetEnableCircularTraceSession2(
    /* [in] */ __RPC__in LPCWSTR machineName,
    /* [out][retval] */ __RPC__out BOOLEAN *enableCircularTraceSession)
{
    Common::DllConfig::GetConfig();

    ErrorCode error = FabricEnvironment::GetEnableCircularTraceSession(machineName, *enableCircularTraceSession);
    return ComUtility::OnPublicApiReturn(error.ToHResult());
}

/* [entry] */ HRESULT FabricGetEnableCircularTraceSession(
    /* [out][retval] */ __RPC__out BOOLEAN *enableCircularTraceSession)
{
    return FabricGetEnableCircularTraceSession2(NULL, enableCircularTraceSession);
}

#ifndef PLATFORM_UNIX
namespace
{
    INIT_ONCE InitOnce = INIT_ONCE_STATIC_INIT;
    DWORD SymInitializeError = 0;
    auto SymPaths = make_global<string>();

    BOOL CALLBACK InitOnceFunction(PINIT_ONCE, PVOID, PVOID *)
    {
        wstring symPath;
        Environment::GetEnvironmentVariable(L"_NT_SYMBOL_PATH", symPath, NOTHROW());
        wstring symPathAlt;
        Environment::GetEnvironmentVariable(L"_NT_ALTERNATE_SYMBOL_PATH", symPathAlt, NOTHROW());
        auto symPathsW = wformatString("{0};{1};{2};{3}", Environment::GetExecutablePath(), Directory::GetCurrentDirectory(), symPath, symPathAlt);
        StringUtility::Utf16ToUtf8(symPathsW, *SymPaths);

        SymInitialize(GetCurrentProcess(), SymPaths->c_str(), TRUE);

        return TRUE;
    }
}

/* [entry] */ LPCSTR FabricSymInitializeOnce()
{
    auto initStatus = ::InitOnceExecuteOnce(
        &InitOnce,
        InitOnceFunction,
        nullptr,
        nullptr);

    Invariant(initStatus);
    return SymPaths->c_str();
}

#endif

namespace
{
    void(*CrashLeasingApplicationCallback) (void) = nullptr;
}

/* [entry] */ void FabricSetCrashLeasingApplicationCallback(
    __RPC__in void (*callback))
{
    CrashLeasingApplicationCallback = (void (*) (void)) callback;
}

/* [entry] */ void FabricGetCrashLeasingApplicationCallback(
    __RPC__out void *(*callback))
{
    *callback = (void *(*)) InterlockedExchangePointer(((void *(*))&CrashLeasingApplicationCallback), nullptr);
}
